#pragma once

#include <AVUIMarkupExtension.h>
#include <AVUILoadInitialize.h>

namespace AVUI {
class Assembly;

/// <summary>Provides an abstract base class for various resource keys. </summary>
class ResourceKey : public MarkupExtension
{
public:
    DECLARE_ELEMENT(ResourceKey, MarkupExtension);

    /// <summary>Returns this <see cref="T:System.Windows.ResourceKey" />. Instances of this class are typically used as a key in a dictionary. </summary>
    /// <returns>Calling this method always returns the instance itself.</returns>
    /// <param name="serviceProvider">A service implementation that provides the desired value.</param>
    TRefCountedPtr<Object> ProvideValue(IServiceProvider* pServiceProvider) AVUI_OVERRIDE { return this; }

    /// <summary>Gets an assembly object that indicates which assembly's dictionary to look in for the value associated with this key. </summary>
    /// <returns>The retrieved assembly, as a reflection class.</returns>
    virtual Assembly* get_Assembly() = 0;
 
protected:
    /// <summary> Initializes a new instance of a class derived from <see cref="T:System.Windows.ResourceKey" />. </summary>
    ResourceKey() { };
};

/// <summary>When used as a resource key for a data template, allows the data template to participate in the lookup process.</summary>
class TemplateKey : public ResourceKey, public ILoadInitialize
{
public:
    DECLARE_ELEMENT(TemplateKey, ResourceKey);

    virtual void BeginInit() { m_initializing = true; }
    virtual void EndInit() { if(m_pDataType == NULL) LibraryCriticalError(); m_initializing = false; }

    /// <summary>Gets or sets the assembly that contains the template definition.</summary>
    /// <returns>The assembly in which the template is defined.</returns>
    virtual Assembly* get_Assembly() { return NULL; }

    /// <summary>Gets or sets the type for which the template is designed. </summary>
    /// <returns>The <see cref="T:System.Type" /> that specifies the type of object that the template is used to display, or a string that specifies the XML tag name for the XML data that the template is used to display.</returns>
    Object* get_DataType() { return m_pDataType; }
    void set_DataType(Object* pValue) { m_pDataType = pValue; }

protected:

    /// <summary>Describes the different types of templates that use <see cref="T:System.Windows.TemplateKey" />.</summary>
    class TemplateType
    {
    public:
        enum Enum
        {
            DataTemplate,
            TableTemplate
        };
    };

    /// <summary>Initializes a new instance of the <see cref="T:System.Windows.TemplateKey" /> class with the specified template type. This constructor is protected.</summary>
    /// <param name="templateType">A <see cref="T:System.Windows.TemplateKey.TemplateType" /> value that specifies the type of this template.</param>
    TemplateKey(TemplateType::Enum templateType) : m_templateType(templateType) { }

    /// <summary>Initializes a new instance of the <see cref="T:System.Windows.TemplateKey" /> class with the specified parameters. This constructor is protected.</summary>
    /// <param name="templateType">A <see cref="T:System.Windows.TemplateKey.TemplateType" /> value that specifies the type of this template.</param>
    /// <param name="dataType">The type for which this template is designed.</param>
    TemplateKey(TemplateType::Enum templateType, Object* pDataType) : m_templateType(templateType), m_pDataType(pDataType) { }

    // Internal
public:

    static TRefCountedPtr<Exception> ValidateDataType(Object* pDataType, const char* szArgName)
    {
        if (pDataType == NULL)
        {
            return object_allocate<ArgumentNullException>(szArgName).get_Ptr();
        }
        if (!is_type<Type>(pDataType) && !is_type<String>(pDataType))
        {
            return object_allocate<ArgumentException>("Must be type or string").get_Ptr();
        }
        return NULL;
    }

private:
    TRefCountedPtr<Object> m_pDataType;
    bool m_initializing;
    TemplateType::Enum m_templateType;

};

/// <summary>Represents the resource key for the <see cref="T:System.Windows.DataTemplate" /> class.</summary>
class DataTemplateKey : public TemplateKey
{
public:
    DECLARE_ELEMENT(DataTemplateKey, TemplateKey);

    /// <summary>Initializes a new instance of the <see cref="T:System.Windows.DataTemplateKey" /> class.</summary>
    DataTemplateKey() : TemplateKey(TemplateKey::TemplateType::DataTemplate) { }

    /// <summary>Initializes a new instance of the <see cref="T:System.Windows.DataTemplateKey" /> class with the specified type.</summary>
    /// <param name="dataType">The type for which this template is designed. This is either a <see cref="T:System.Type" /> (to indicate that the <see cref="T:System.Windows.DataTemplate" /> is used to display items of the given type), or a string (to indicate that the <see cref="T:System.Windows.DataTemplate" /> is used to display <see cref="T:System.Xml.XmlNode" /> elements with the given tag name).</param>
    DataTemplateKey(Object* pDataType) : TemplateKey(TemplateKey::TemplateType::DataTemplate, pDataType) { }
};





}; // namespace AVUI

